package com.googlecode.gxtmvp.rest.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.rpc.AsyncCallback;

import java.util.Map;
import java.util.Map.Entry;

public class RestService {
  
  public <D, C> void send(final RestCommand<D, C> command, final AsyncCallback<D> callback) {
    final String url = hostPageBaseURL() + encode(command.getUri()) + encodeParams(command.getParams());
    final RequestBuilder requestBuilder = new MyRequestBuilder(command.getMethod(), url);
    final Codec<String, C, D> codec = command.getCodec();
    if (codec != null) {
      requestBuilder.setHeader("Content-Type", codec.getContentType());
    }
    final String content = (codec == null) ? "" : codec.encode(command.getConfig(), command.getData());
    try {
      requestBuilder.sendRequest(content, new RequestCallback() {
        @Override
        public void onError(final Request request, final Throwable exception) {
          callback.onFailure(exception);
        }
        
        @Override
        public void onResponseReceived(final Request request, final Response response) {
          final int statusCode = response.getStatusCode();
          if (statusCode >= 200 && statusCode < 300) {
            callback.onSuccess((codec == null) ? null : codec.decode(command.getConfig(), response.getText()));
          } else {
            callback.onFailure(new RequestException("" + response.getStatusCode()));
          }
        }
      });
    } catch (final RequestException e) {
      callback.onFailure(e);
    }
  }
  
  protected String encodeParams(final Map<String, String> params) {
    if (params.size() == 0) {
      return "";
    }
    final StringBuilder result = new StringBuilder("?");
    boolean firstPassed = false;
    for (final Entry<String, String> entry : params.entrySet()) {
      if (firstPassed) {
        result.append('&');
      }
      result.append(encodeUrlComponent(entry.getKey())).append('=').append(encodeUrlComponent(entry.getValue()));
      firstPassed = true;
    }
    return result.toString();
  }
  
  String encode(final String string) {
    return URL.encode(string);
  }
  
  String encodeUrlComponent(final String decodedString) {
    return URL.encodeComponent(decodedString);
  }
  
  String hostPageBaseURL() {
    return GWT.getHostPageBaseURL();
  }
  
}
